import type { Graph } from "../graph/graph";
import type { INode } from "../graph/node/instance";
import { PNodeType } from "../graph/node/type";
import type { ISlot } from "../graph/slot/instance";
import { pyFormat } from "../pyCode/PyData";
import { sprintf } from 'sprintf-js'

export class NodeCompiler {

  /**
   * compileNode 编译Node实例，其中包含了各种节点的编译策略
   * @param iNode 目标Node实例
   * @param root Node实例所处的图
   * @returns 
   */
  static compile(iNode: INode, root: Graph): string {
    const proto = iNode.prototype
    // console.log(`编译了 Node - ${iNode.prototype.name}`);

    if (proto.type === PNodeType.Function) {
      const data = iNode.getInDataSlots().map(x => parseInData(x, root))
      const outParam = iNode.getOutDataSlots().map(s => `${s.prototype.name}_${iNode.id}`)
      if (outParam.length) {
        // console.log(`Node${iNode.prototype.name}返回了: ${outParam.join(', ')} = ${proto.name}(${data.join(', ')})`);
        return `${outParam.join(', ')} = ${proto.name}(${data.join(', ')})`
      }
      else {
        // console.log(`Node${iNode.prototype.name}返回了: ${proto.name}(${data.join(', ')})`)
        return `${proto.name}(${data.join(', ')})`
      }
    }
    else if (iNode.prototype.type === PNodeType.Process) {
      if (proto.name === 'Start') { return '' }
      else if (proto.name === 'End') {
        const dataList = iNode.getInDataSlots()
          .map(x => parseInData(x, root))
          // console.log(`Node${iNode.prototype.name}返回了: return ${dataList.join(', ')}`)
        return `return ${dataList.join(', ')}`
      }
      else {
        const temp = iNode.prototype.processTemplate
        if (!temp) {
          console.error(iNode.prototype);
          throw new Error(`编译了过程节点，但是缺少模板`)
        }
        console.log('///////');
        console.log(iNode.getInDataSlots());
        
        const data = iNode.getInDataSlots().map(x => parseInData(x, root))
        const returns = iNode.getOutDataSlots().map(x => `${x.prototype.name}_${iNode.id}`)

        // console.log(`Node${iNode.prototype.name}返回了: ${sprintf(temp, { data })}`)
        return sprintf(temp, { data, returns })
      }
    }
    else if (iNode.prototype.type === PNodeType.Expression) {
      const symbol = iNode.prototype.title
      const data: string[] = iNode.getInDataSlots().map(x => parseInData(x, root))
      // console.log(`Node${iNode.prototype.name}返回了: (${data.join(symbol)})`)
      return `(${data.join(symbol)})`
    }
    else if (iNode.prototype.type === PNodeType.PureFunc) {
      const data = iNode.getInDataSlots().map(x => parseInData(x, root))
      // console.log(`Node${iNode.prototype.name}返回了: ${proto.name}(${data.join(', ')})`)
      return `${proto.name}(${data.join(', ')})`
    }
    else if (iNode.prototype.type === PNodeType.Setter) {
      // console.log(`编译了 Setter - ${iNode.prototype.name}`);
      
      const symbol = iNode.prototype.name
      const data = parseInData(iNode.getInDataSlots()[0], root)
      if (symbol.startsWith('global_')) {
        const _symbol = symbol.slice(7)
        // console.log(`返回 - global ${_symbol}; ${_symbol} = ${data}`);
        return `global ${_symbol}; ${_symbol} = ${data}`
      }
      else {
        // console.log(`返回 - ${symbol} = ${data}`);
        return `${symbol} = ${data}`
      }
    }

    // 未知节点
    console.error(iNode);
    throw new Error(`当前节点的编译方法尚未实现。`)
  }
}


export function parseInData(slot: ISlot, root: Graph): string {
  console.log('parseInData', slot);
  
  if (slot.prototype.direction === 'out') {
    throw new Error('parseInData: slot方向错误, 本函数用来获取输入槽的表达式')
  }
  if (slot.data !== undefined) {
    return pyFormat(slot.data.content)
  }
  else {
    const peer = slot.peer
    if (!peer) {
      throw new Error('parseInData: 槽没有常量写入，也没有连接')
    }
    const peerNode = peer.findparent(root)
    if (!peerNode) {
      throw new Error('parseInData: peerNode寻找失败')
    }

    // 表达式不执行，纯函数行内联执行，取他们的结果相当于直接把语句塞进去
    if ([PNodeType.Expression, PNodeType.PureFunc].includes(peerNode.prototype.type)) {
      return NodeCompiler.compile(peerNode, root)
    }
    // 对于独立执行的语句，为了防止产生的返回值/符号重复。为返回值加上独一无二的id
    else if (
      peerNode.prototype.type === PNodeType.Function
      || peerNode.prototype.type === PNodeType.Process
    ) {
      return `${peer.prototype.name}_${peerNode.id}`
    }
    else {
      return peer.prototype.name
    }
  }
}